Android ServiceManager和它的兄弟们
本文摘要
本篇文章同样采用自述和对话的方式,来介绍servicemanager、vndservicemanager、hwservicemanager 这三个系统native进程,通过本文您将了解到为什么要有vndservicemanager和hwservicemanager进程、以及它们在Android进程之间通信到底起了哪些作用?(基于android13代码分析)
Part1 我家有三宝
大家好啊,我是init还记得我吧,上次和大家见面还是在我是init这篇文章,确实有很长的时间了,为了让大家依然记得我,我再次出现并且还带了我的三宝servicemanager、vndservicemanager、hwservicemanager来与大家认识。
vold有点嫉妒的说:“我的老父亲,你纵有儿孙上百上千,也没有见你宠过谁啊,为啥今天你就独宠它们三呢?并且还称呼它们三为三宝。你这可是一碗水没端平啊,需要给我们一个解释。”
init:“vold no no no,你完全误解我了,我可没有偏心谁啊,称呼它们三为三宝,主要是因为它们都解决了同样的问题: 那就是进程之间的通信问题。既然提到了进程,那我再次把我的子子孙孙展示出来 (如下图)。”
servicemanager气急败坏的说:“老爹啊!这节的重点不是我们三兄弟吗?怎么到现在了你还在炫耀你的子孙,是不是忽略我们了。”
init:“是这样的,在不同进程之间通信是用到了binder、vndbinder、hwbinder这三种类型binder进行通信,而servicemanager你们兄弟是服务于不同的binder。哪种类型进程之间使用哪种类型binder,这完全和进程分类有关,因此有必要先把我的子子孙孙展示出来,先看下它们是如何分类的。”
看到没,我的家族是非常庞大的,首先我把我的子孙分为两大类:java进程和native进程。java进程就是可运行java代码的进程,它又可以划分为systemserver进程、系统app进程、app进程;native进程则自然就是只能运行c/c++代码的进程,它又可以划分为系统native进程、普通native进程、hal进程 (hal进程大家应该第一次听说,后面会着重介绍它)
既然进程按类划分了,那来看下它们之间是使用哪种类型的binder进行通信的:
java进程之间通信、java与native进程 (除hal进程)之间通信、native进程 (除hal进程)之间通信都用binder进行通信 非hal进程与hal进程之间通信使用hwbinder进行通信 hal进程之间通信使用vndbinder进行通信
不管是hwbinder还是vndbinder它们其实都是binder的变种,它们的核心都还是binder只不过换了一身皮而已。(至于为啥要有hwbinder、vndbinder我先卖个关子,后面会介绍)
而servicemanager在binder通信中的作用犹如DNS,DNS 域名解析服务给它一个域名是可以查出对应的ip地址。在网页中输入某个网址后,需要去DNS查询该网址的ip地址,进而根据ip地址请求真实的服务器。而servicemanager也正是如此作用,比如app进程想调用ActivityManagerService (ActivityManagerService位于systemserver进程)的能力,则app进程需要从servicemanager获取到对方的“联系号码” (这里的联系号码不是真正意义上的电话的电话号码),拿到“联系号码”后方可以与对方进行binder通信。
servicemanager正如其名它是一个service管理者,它的作用就如DNS,谁要想与哪个进程进行binder通信,需要从servicemanager拿到对方的“联系号码”,拿到这个“联系号码”就可进行binder通信了。而binder通信又分为binder、vndbinder、hwbinder三种,而我家的三宝servicemanager、vndservicemanager、hwservicemanager又分别服务于不同的binder通信。
我想大家应该都有个疑惑吧就是已经有了servicemanager了,为啥还要有vndservicemanager和hwservicemanager呢?hal进程又是啥?至于原因就交给vndservicemanager和hwservicemanager给大家解答吧。
具体它们三兄弟都提供了哪些能力,就交给它们给大家介绍吧,那我就把舞台交给我家的三个宝贝,由它们来给大家展示。
vold:“都宝贝宝贝叫了,还说不偏心。”
Part2 我家老大
servicemanager:“各位父老乡亲,大家好啊,我是servicemanager,像vold、installd一样我也是一个系统native进程,大名鼎鼎的binder通信大家都知道吧,我就是为binder通信而生的。binder通信是Client/Server模式,binder client是可以调用binder server提供的能力的。”
init:“儿啊!我插一句,我知道socket用java实现的话,ServerSocket类代表server端,Socket类代表client端,那binder的client和server又是啥呢?”
servicemanager:“老爹是这样的,因为咱们不是binder通信专场,因此我简单介绍下。在Android的java层和native层都会存在binder server,在java层的话binder server是Binder类,在native层的话binder server是BBinder类,Binder类是对BBinder的封装。而binder client若要使用某个具名binder server的能力的话,是需要从我的‘接口人’ServiceManager获取对应信息的,只有拿到对应信息才可以与对应binder server通信。”
init:“有点糊涂了,这里的具名binder server是啥?这里的信息又指啥?”
servicemanager:“哈哈,都是我的错,我偷懒了,具名就是具有名字的简称,具名binder server就是在我的‘接口人’ServiceManager添加过的,是可以从ServiceManager获取到的 (比如通过字符串activity可以查到ActivityManagerService),有具名那自然有匿名binder server了,匿名binder server是没有在ServiceManager添加过的,是从ServiceManager查不到的。”
“再来说说信息是啥?咱们需要先有一个前提条件就是binder client和具名binder server是两个不同的进程,信息在java层指的是BinderProxy类,在native层指的是BpBinder类,这样就可以做到在binder client犹如调用当前进程的某个方法。BinderProxy类同样也是对BpBinder类的封装。而BpBinder最核心的属性是一个int类型名为handle的属性,在binder驱动层是可以通过handle找到对应的binder node (binder node封装了上层的binder server),进而通过binder node找到对应的binder server。”
“我是家里的老大,为啥我是老大?因为我诞生的最早啊,在Android系统刚出来的时候我就一起出现了,而我的那俩兄弟是在Android8后才出现,因此我是老大。”
我的名字叫servicemanager,是一个系统native进程,我的“接口人”ServiceManager是一个binder server ,它管理着所有的具名binder server,你们binder client在要调用某个具名binder server的能力之前,是需要从ServiceManager获取对应具名binder server的BpBinder或者BinderProxy实例,通过实例才可以与对应binder server通信,我主要是为非hal进程之间进行binder通信服务的。
出生
我的父亲是init进程,我的父亲是一个“不称职的父亲“,为啥这样说呢,它对于我何时创建、创建后叫啥名字等这些信息,它统统不知道,你们说它负责吗,我只需要把这些信息用init脚本语言配置好后交给它即可,剩下的事就全权交给它了,脚本语言配置的信息如下:
//文件路径:/frameworks/native/cmds/servicemanager/servicemanager.rc
service servicemanager /system/bin/servicemanager
class core animation
user system
group system readproc
critical
onrestart restart apexd
onrestart restart audioserver
onrestart restart gatekeeperd
onrestart class_restart --only-enabled main
onrestart class_restart --only-enabled hal
onrestart class_restart --only-enabled early_hal
task_profiles ServiceCapacityLow
shutdown critical
在init.rc文件中会start servicemanager命令,这时候init进程会fork子进程 (子进程的name是servicemanager) ,并且会执行/system/bin/servicemanager可执行文件,该可执行文件最终会执行下面的方法
//文件路径:/frameworks/native/cmds/servicemanager/main.cpp
int main(int argc, char** argv) {
省略代码......
}
我刚刚“出生”,犹如一个刚出生的婴儿,还没有说话、吃饭、走路的能力,那就来看下我是如何“长大成人”的。
长大成人
打开binder驱动
长大成人的第一步是调用ProcessState::initWithDriver方法打开binder驱动,而该方法的参数是/dev/binder,打开binder驱动所做的事情如下:
binder驱动会把servicemanager进程的信息pid、uid (uid也就是app安装时候生成的id)等关键信息记录下来,并且会返回一个fd (fd文件描述符,是一个int类型),在后面与binder驱动通信过程中都需要带上该fd (通过它可以找到记录下来的信息) 进行mmap操作 (用户空间与内核空间进行内存映射,这也是binder通信只需一次拷贝的关键) 构建并返回ProcessState对象,在一个进程中只存在一个实例
如下部分代码,请自行取阅
//文件路径:/frameworks/native/cmds/servicemanager/main.cpp
int main(int argc, char** argv) {
if (argc > 2) {
LOG(FATAL) << "usage: " << argv[0] << " [binder driver]";
}
//argc==2则使用argv[1],否则使用/dev/binder驱动,当前servicemanager进程会使用/dev/binder
const char* driver = argc == 2 ? argv[1] : "/dev/binder";
//该方法会打开binder驱动
sp<ProcessState> ps = ProcessState::initWithDriver(driver);
省略代码......
}
配置binder最大线程数量
别看我servicemanager是一个进程,但是我的“接口人”ServiceManager所提供的功能是非常简单的,因此是非常没有必要启动那么多的线程的,因此需要调用ProcessState::setThreadPoolMaxThreadCount方法 (参数为0),该方法所做的事情如下:
告知binder驱动我servicemanager进程配置的最大线程数是0 如果设置成功了,ProcessState也会更新最大线程数
binder驱动会检测当前进程是否有空余的binder线程,若没有的话binder驱动会通知当前进程应该创建binder线程了,而这个通知是需要结合最大线程数来决定的,如果已经创建的线程数量等于最大线程数,则binder驱动肯定是不会通知当前进程的。而最大线程数设置为0,就是binder驱动根本不会通知servicemanager进程创建binder线程,也就是servicemanager进程只需要一个线程就足以处理所有的binder请求。
如下部分代码,请自行取阅
//文件路径:/frameworks/native/cmds/servicemanager/main.cpp
int main(int argc, char** argv) {
省略代码......
//设置最大线程数
ps->setThreadPoolMaxThreadCount(0);
省略代码......
}
构建ServiceManager
终于到了构造我的“接口人”ServiceManager这一步了,它是真正功能的提供者,构建ServiceManager是非常简单的,只需要一个Access对象即可 (Access做权限校验)。
当然构建完毕ServiceManager会调用它自己的addService方法 (key为字符串manager) 把自己添加到ServiceManager。
如下部分代码,请自行取阅
//文件路径:/frameworks/native/cmds/servicemanager/main.cpp
int main(int argc, char** argv) {
省略代码......
sp<ServiceManager> manager = sp<ServiceManager>::make(std::make_unique<Access>());
if (!manager->addService("manager", manager, false /*allowIsolated*/, IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk()) {
LOG(ERROR) << "Could not self register servicemanager";
}
IPCThreadState::self()->setTheContextObject(manager);//manager会处理各种transaction
省略代码......
}
成为管理者
上面提到过我的“接口人”ServiceManager它不是一个普通的binder server,大家上眼看它的名字中带有manager,也就是它是一个管理者管理谁呢?那当然是所有的具名binder server了,binder server分为具名和匿名两种,具名就是有名字的如ActivityManagerService等,匿名就是没名字的比如在app中new了一个BInder对象。
成为管理者需要调用ProcessState::becomeContextManager方法,该方法会与binder驱动通信,binder驱动会把当前进程标记为manager,只有binder驱动标记成功后才代表ServiceManager成为管理者 (这里的标记做的事情可不是只有标记这么简单)
如下部分代码,请自行取阅
//文件路径:/frameworks/native/cmds/servicemanager/main.cpp
int main(int argc, char** argv) {
省略代码......
ps->becomeContextManager();
省略代码......
}
成人
我敢非常自信骄傲的对大家说:我是王炸级别的存在,为啥呢?因为我即将成人的时候就已经是管理者了,问世间有几个人能做到。
你们人类成人后,每天工作完还会休息,而我可不是我是7*24小时全年无休,要与你们人类竞选劳模,我绝对是杠杠硬。那就来看下我是如何做到全年无休的,其实很简单就是启动一个无限循环。
如下代码
//文件路径:/frameworks/native/cmds/servicemanager/main.cpp
int main(int argc, char** argv) {
省略代码......
//当前线程创建Looper
sp<Looper> looper = Looper::prepare(false /*allowNonCallbacks*/);
BinderCallback::setupTo(looper);
ClientCallbackCallback::setupTo(looper, manager);
//进入无限循环
while(true) {
looper->pollAll(-1);
}
}
上面代码,在当前线程会创建一个Looper (Looper大家肯定不陌生),并且开启一个无限循环,Looper会监听打开binder驱动返回的fd上是否有数据,有的话就会把对应的消息传递给ServiceManager,而fd上的数据是binder驱动写入的,那啥时候写入呢?当然是binder client调用ServiceManager能力的时候。
从此我进入了全年无休的状态,真是悲催啊!
init:“servicemanager儿啊,作为进程要诚实啊,整个Android系统用户空间谁的寿命最长,你那能叫全年无休吗?当reboot的时候,你是不是就死掉了。别把自己的工作程度如此夸大好吧。”
servicemanager:“老爹别了,都啥年代了,当今年代不吹点牛、不夸张点没办法赚money啊!”
下面是与“长大成人”相关的部分关键代码,自行取阅
//文件路径:/frameworks/native/cmds/servicemanager/main.cpp
int main(int argc, char** argv) {
//获取驱动名称。如果argc==2,则使用argv[1],argv[1]为 /dev/vndbinder,否则使用 /dev/binder
const char* driver = argc == 2 ? argv[1] : "/dev/binder";
sp<ProcessState> ps = ProcessState::initWithDriver(driver);
ps->setThreadPoolMaxThreadCount(0);
ps->setCallRestriction(ProcessState::CallRestriction::FATAL_IF_NOT_ONEWAY);
sp<ServiceManager> manager = sp<ServiceManager>::make(std::make_unique<Access>());
if (!manager->addService("manager", manager, false /*allowIsolated*/, IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk()) {
LOG(ERROR) << "Could not self register servicemanager";
}
IPCThreadState::self()->setTheContextObject(manager);//manager会处理各种transaction
ps->becomeContextManager();
sp<Looper> looper = Looper::prepare(false /*allowNonCallbacks*/);
BinderCallback::setupTo(looper);
ClientCallbackCallback::setupTo(looper, manager);
while(true) {
looper->pollAll(-1);
}
// should not be reached
return EXIT_FAILURE;
}
如何得到我
如何得到我指的是如何获取我的“接口人”ServiceManager,因为它是一个binder server,因此在与它通信之前是需要获取到它的BpBinder或者BinderProxy实例的,那应该从哪里获取呢?
答案可以从DNS获得启发,浏览器一般会设置DNS服务器的默认ip地址,这样在解析域名的时候会去这个ip地址进行解析,而这个ip地址是一个预先填好的值,ok那借着这个思路,可以把ServiceManager的BpBinder的handle值设置为0,那binder驱动在检测到handle值为0的时候,进而找到servicemanager进程,进而把消息发送给ServiceManager。(在成为管理者的时候调用ProcessState::becomeContextManager方法让binder驱动标记把ServiceManager标记为了管理者)
因此binder client要与ServiceManager进行通信,其实就是构造一个handle为0的BpBinder或者BinderProxy即可,是不是非常简单。
那来介绍下ServiceManager提供的能力吧。
添加服务
我既然作为具名binder server的管理者,那这些binder server是如何加入的呢?答案是我提供addService能力,你们binder server如果谁想要被binder client使用,请调用addService方法啊,该方法需要两个关键参数字符串name和IBinder。
name作为key值 ,这也是具名binder server的由来,添加时候需要name,获取的时候也需要name。
而IBinder是BpBinder或者BBinder。如果调用addService方法的进程不是servicemanager进程,则IBinder是BpBinder类,因为binder驱动会把别的进程的BBinder或者BpBinder进行转换为BpBinder。(如下方法定义)
Status ServiceManager::addService(const std::string& name, const sp<IBinder>& binder, bool allowIsolated, int32_t dumpPriority) {
省略代码......
}
如果你们不调用该能力,binder client找不到你们的BpBinder或者BinderProxy可不是我的责任啊,那是你们不听话。
存储服务用的是mNameToService这个属性,它是map类型,name作为map的key,用Service数据结构把IBinder等信息封装起来。(如下定义)
//Service定义
struct Service {
sp<IBinder> binder; // not null
bool allowIsolated;
int32_t dumpPriority;
bool hasClients = false; // notifications sent on true -> false.
bool guaranteeClient = false; // forces the client check to true
pid_t debugPid = 0; // the process in which this service runs
// the number of clients of the service, including servicemanager itself
ssize_t getNodeStrongRefCount();
};
using ServiceMap = std::map<std::string, Service>;
ServiceMap mNameToService;
下面是添加服务部分代码,请自行取阅
//文件路径:/frameworks/native/cmds/servicemanager/ServiceManager.cpp
Status ServiceManager::addService(const std::string& name, const sp<IBinder>& binder, bool allowIsolated, int32_t dumpPriority) {
省略掉权限检查相关的代码......
// Overwrite the old service if it exists
mNameToService[name] = Service {//构造Service对象,若存在会把原先的覆盖
.binder = binder,
.allowIsolated = allowIsolated,
.dumpPriority = dumpPriority,
.debugPid = ctx.debugPid,
};
auto it = mNameToRegistrationCallback.find(name);
if (it != mNameToRegistrationCallback.end()) {
for (const sp<IServiceCallback>& cb : it->second) {
mNameToService[name].guaranteeClient = true;
// permission checked in registerForNotifications
cb->onRegistration(name, binder);//告知监听者监听的name和binder已经注册了
}
}
return Status::ok();
}
移除服务
既然有添加肯定要有移除啊,如果不移除会有啥问题呢?在讨论有啥问题之前先来介绍下binder server的生死。
binder server其实也是有生死的,当binder server死掉的时候,binder驱动会通知所有的正在使用该binder server的binder client,通过DeathRecipient的binderDied方法告知binder client它使用的binder server已经死掉了。
如果不从ServiceManager的mNameToService中把死掉的service移除掉,则首先返回给binder client的BpBinder是有问题的,因为它的binder server已经死掉了。并且如果不移除的话,内存也会不断地增长,白白的浪费内存 (因为存储了一堆没有的数据)
当binder server死掉的时候ServiceManager的binderDied方法会被binder驱动调用,进而把对应的IBinder从mNameToService中移除掉。
下面是部分关键代码,请自行取阅
//文件路径:/frameworks/native/cmds/servicemanager/ServiceManager.cpp
void ServiceManager::binderDied(const wp<IBinder>& who) {
//从mNameToService中进行查找,找到了则从mNameToService中移除掉
for (auto it = mNameToService.begin(); it != mNameToService.end();) {
if (who == it->second.binder) {
it = mNameToService.erase(it);
} else {
++it;
}
}
省略代码......
}
获取服务
binder client是可以通过ServiceManager的getService方法获取到对应的服务,该方法需要两个关键参数字符串name和out型的IBinder。如下方法定义:
Status ServiceManager::getService(const std::string& name, sp<IBinder>* outBinder) {
*outBinder = tryGetService(name, true);
// returns ok regardless of result for legacy reasons
return Status::ok();
}
binder client通过该方法获取的IBinder有可能是BpBinder也有可能是BnBinder。如果调用该方法是在servicemanager进程内调用的并且add的时候IBinder本身就是BBinder则返回该BBinder类实例;否则返回BpBinder实例。
如果通过name没有找到对应的IBinder,则会尝试启动对应的binder server,尝试启动是让init进程去尝试启动,这种情况其实有一个非常大的好处就是可以实现binder server的懒加载,只有真正有binder client需要binder server的时候才去启动它,这样可以大大的节省内存。(如下代码)
void ServiceManager::tryStartService(const std::string& name) {
ALOGI("Since '%s' could not be found, trying to start it as a lazy AIDL service",
name.c_str());
std::thread([=] {
//调用SetProperty方法后,init进程会收到"ctl.interface_start"属性的变化,进而开始启动对应的service
if (!base::SetProperty("ctl.interface_start", "aidl/" + name)) {
LOG(INFO) << "Tried to start aidl service " << name
<< " as a lazy service, but was unable to. Usually this happens when a "
"service is not installed, but if the service is intended to be used as a "
"lazy service, then it may be configured incorrectly.";
}
}).detach();
}
异步获取服务
ServiceManager的getService方法是一个同步方法,比如当前如果对应的binder server确实是没有添加到ServiceManager的mNameToService,则会尝试让init进程去启动该binder server,并且getService方法会返回null,这时候binder client获取的是一个null值。但是init进程在后台启动了binder server,并且它启动后调用了ServiceManager的addService方法,把自己加入了mNameToService,但binder client却完全不知道这些事情,那该咋办呢?。
那针对以上这种情况,提供了一种解决方案:ServiceManager有一个方法registerForNotifications,它可以为某个binder server的name注册一个IServiceCallback。用更直白的话说就是ServiceManager提供了回调能力,binder client可以注册某个binder server的回调,当对应的binder server添加到ServiceManager后就会通过该回调告知binder client。
有没有使用者
有没有使用者是啥意思呢?就是说binder server是可以从ServiceManager知道是否有binder client在使用它,是不是一个非常实用的功能啊。
ServiceManager的registerClientCallback方法实现了该功能,具名binder server可以调用该方法来监测是否有binder client在使用自己。如下是该方法的定义
Status ServiceManager::registerClientCallback(const std::string& name, const sp<IBinder>& service,
const sp<IClientCallback>& cb) {
省略各种校验的代码......
//mNameToClientCallback也是一个map,key值为name,value为IClientCallback
mNameToClientCallback[name].push_back(cb);
return Status::ok();
}
我servicemanager会每隔5s来监测所有的具名binder server是否有binder client,有或者没有会把信息发送给对应binder server,下面是相关代码,请自行取阅
//文件路径:/frameworks/native/cmds/servicemanager/main.cpp
int main(int argc, char** argv) {
省略代码.......
//main方法中会调用ClientCallbackCallback的setupTo方法初始化,ClientCallbackCallback类setupTo方法请查看[1.1]
ClientCallbackCallback::setupTo(looper, manager);
while(true) {
looper->pollAll(-1);
}
// should not be reached
return EXIT_FAILURE;
}
class ClientCallbackCallback : public LooperCallback {
public:
[1.1]
static sp<ClientCallbackCallback> setupTo(const sp<Looper>& looper, const sp<ServiceManager>& manager) {
sp<ClientCallbackCallback> cb = sp<ClientCallbackCallback>::make(manager);
//创建clock对应的fd
int fdTimer = timerfd_create(CLOCK_MONOTONIC, 0 /*flags*/);
//间隔5s
itimerspec timespec {
.it_interval = {
.tv_sec = 5,
.tv_nsec = 0,
},
.it_value = {
.tv_sec = 5,
.tv_nsec = 0,
},
};
省略代码......
//looper监听fdTimer,每5s会执行一次cb也就是下面的handleEvent方法
int addRes = looper->addFd(fdTimer,
Looper::POLL_CALLBACK,
Looper::EVENT_INPUT,
cb,
nullptr);
return cb;
}
//cb会执行该方法
int handleEvent(int fd, int /*events*/, void* /*data*/) override {
省略代码......
//每5s执行一次该方法,handleClientCallbacks请查看[1.2]
mManager->handleClientCallbacks();
return 1; // Continue receiving callbacks.
}
省略属性代码......
};
[1.2]
//文件路径:/frameworks/native/cmds/servicemanager/ServiceManager.cpp
void ServiceManager::handleClientCallbacks() {
//遍历所有添加的binder server
for (const auto& [name, service] : mNameToService) {
handleServiceClientCallback(name, true);
}
}
总结
我的名字是servicemanager,是一个系统native进程,我的“接口人”ServiceManager它是一个binder server,它管理着很多具名binder server。
ServiceManager提供了addService方法,具名binder server调用它就可以把自己添加到ServiceManager的mNameToService属性中。
ServiceManager提供了registerClientCallback方法,具名binder server可以知道有没有binder client在使用它。
ServiceManager提供了getService方法,binder client若想使用某个具名binder server,就可以调用该方法并且需要传递很关键的name参数,这样就可以返回BpBinder或者BinderProxy (这里基于的前提是调用getService方法的进程与servicemanager不是同一进程)
ServiceManager提供了registerForNotifications方法,binder client可以设置callback,当某个具名binder server添加到ServiceManager的mNameToService属性中,会通过该callback把BpBinder或者BinderProxy发送给binder client (这里基于的前提也是调用registerForNotifications方法的进程与servicemanager不是同一进程)
这就是我,那接下来有请我家老二吧。+
Part3 我家老二
hwservicemanager:“各位父老乡亲,大家好,我是hwservicemanager,hw是hardware的缩写,我像我哥servicemanager一样,我也是一个系统native进程。关于我就不需要做更多的介绍了,因为我和我哥是一样的,只不过换了一身皮而已。我主要是为非hal进程与hal进程进行hwbinder通信服务的。hwbinder通信其实和binder通信也是一样的,换了个名字而已。”
init:“老二啊,我有问题要问啊,我记得以前我重来没有hal进程这些孩子的,为啥在android8上有了呢?还有hal进程与非hal进程之间通信为啥不用原有的binder通信?为啥要搞出一套hwbinder通信?”
hwservicemanager:“老爹你这些问题,我觉得大家也存在,因此我就先展开讲讲。”
为何有我
一直在讲hal,那先介绍下hal到底是啥意思吧,如下解释:
hal:Hardware Abstraction Layer的缩写,意思为硬件抽象层,用于提供对硬件设备的抽象层
用更直白的话解释就是:做了一个抽象层,这个抽象层定义了一些通用的接口和数据结构,而具体硬件如何实现的,抽象层不关心,硬件只需要按照抽象层的接口和数据结构进行实现和扩展即可,这就是hal。可以把它与抽象类做对比。
android8之前,android framework的代码和厂商硬件实现代码还有hal层代码都是打在一个system.img镜像文件的。那先来看下android8以前app在使用硬件的功能时候的流程是啥样的,如下图
图解:
app调用某个service的能力比如打开聚光灯、打开振动 对应的service会通过jni调用进入native层代码 native层代码会加载硬件的so,并且调用hal的api hal的api最终会调用厂商的硬件实现 (驱动实现)来执行相应功能
厂商的硬件实现代码与native hal代码是耦合在一起的,若厂商硬件实现发生了改动,则native hal的代码也需要改动。就因为厂商硬件实现代码小小的改动,就需要重新打system.img文件,这对于工作效率的提升以及android系统升级来说是一个非常大的拦路虎。
那该如何解决这问题呢?
第一步:代码层级化或模块化。大家还记得网络七层协议吗,层与层之间是解耦的,这样即使某层内部发生了变化也不会影响它的上层。那我们可以借助这个思想,首先进行层级化或者模块化,因为android framework的代码是google实现的,而hal和厂商硬件实现代码是厂商实现的,所以android framework的代码还是打到system.img镜像文件中,而厂商的代码打到vendor.img镜像文件中。
第二步:层与层或模块与模块之间使用接口交互。在开发大型app的时候,不同部门做着不同的模块,那为了做到高内聚低耦合,就需要定义好一套接口。同样现在也需要定义一套接口,framework层与hal之间使用接口进行交互,这样大大的做到了高内聚低耦合。google官方也推出了hidl hal接口定义语言与aidl很像,同时也支持使用aidl来定义交互接口。
第三步:hal进程化。也就是把一部分hal的功能放在一个单独的进程内实现,因为原先很多的hal的功能是与调用进程处于同一进程的,这样有个坏处就是会在不同的进程内初始化多次并且重复占用过多的内存,hal进程化可以减少这些问题。但是hal进程化并不是完全无缺点,那就是进程与进程之间通信的效率肯定比不上在同一进程内,因此也不是所有的hal都需要进程化的,官网列出了哪些需要进程化哪些不需要。
上面三步只是解决了一部分问题,当然还有system.img与vendor.img之间各种so库调用怎么样解耦,怎么样划分职责等问题就不在这赘述了。
上面提到了hal进程化,那自然就会有另外一个问题就是非hal进程与hal进程通信,google官方特意为它们之间的通信基于binder通信定制了一套hwbinder通信,其实它们俩几乎是一样的。因为binder通信中有servicemanager进程,同样hwbinder通信中有hwservicemanager进程。
这也就是我hwservicemanager产生的由来。
我哥servicemanager打开驱动时候使用的是/dev/binder,而我在打开驱动的时候用的是/dev/hwbinder,我同样也有“接口人”它的名字也是ServiceManager,关于我的“出生”、“长大成人”、“提供的能力”等就不在这赘述了,因为与我哥servicemanager的这些介绍都是一样的,它有的能力我有,他没有的我也没有,其实我的很多代码都是复制了它的代码,在此基础上做了改动。
Part4 我家老三
vndservicemanager:“各位父老乡亲,大家好,我是vndservicemanager,vnd是vendor的缩写,我像我哥servicemanager一样,我也是一个系统native进程。我主要是为hal进程与hal进程进行vdnbinder通信服务的。vndbinder通信其实和binder通信也是一样的,换了个名字而已。我和servicemanager完全用了一套同样的代码,只不过在打开binder驱动的时候使用的是/dev/vndbinder,关于我就介绍到这。”
Part4 总结
我是init进程,由于我有很多的子子孙孙,我把它们划分为两大类java进程和native进程,native进程又被划分为系统native进程和hal进程。
非hal进程之间使用binder通信,而servicemanager进程作为它们之间的服务管理者,servicemanager进程在打开binder驱动时使用/dev/binder。
非hal进程与hal进程之间使用hwbinder通信,hw是hardware(硬件)单词缩写hwservicemanager进程作为它们之间的服务管理者,hwservicemanager进程在打开binder驱动时使用/dev/hwbinder。
hal进程之间使用vndbinder通信,vnd是vendor(厂商)单词缩写,vndservicemanager进程作为它们之间的服务管理者,vndservicemanager进程在打开binder驱动时使用/dev/vndbinder。